﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;

namespace SerialPort2Socket
{
    class SocketServer
    {
        Dictionary<string, Socket> clientConnectionItems = new Dictionary<string, Socket>();

        public string Config { get; set; }
        public Socket ServerSocket { get; set; }
        public SerialPort SerialPort { get; set; }
        public string PortName { get; set; }
        public int BaudRate { get; set; }
        public Parity Parity { get; set; }
        public int DataBits { get; set; }
        public StopBits StopBits { get; set; }
        public SocketType SocketType { get; set; }
        public ProtocolType ProtocolType { get; set; }
        public string IP { get; set; }
        public int Port { get; set; }

        public SocketServer(string config)
        {
            Config = config;
            string pattern = @"(?<portName>COM\d+)\((?<baudRate>\d+),(?<parity>[a-zA-Z]),(?<dataBits>\d),(?<stopBits>\d)\)-(?<protocolType>[a-zA-Z]{3})\((?<ip>.*?):(?<port>\d{1,5})\)";
            var match = Regex.Match(config, pattern, RegexOptions.IgnoreCase);
            if (match.Success)
            {
                PortName = match.Groups["portName"].Value;
                BaudRate = int.Parse(match.Groups["baudRate"].Value);
                DataBits = int.Parse(match.Groups["dataBits"].Value);
                StopBits = EnumHelper.ConvertFromString<StopBits>(match.Groups["stopBits"].Value);
                ProtocolType = match.Groups["protocolType"].Value.Equals("Tcp", StringComparison.OrdinalIgnoreCase) ?
                     ProtocolType.Tcp : ProtocolType.Udp;
                SocketType = ProtocolType == ProtocolType.Tcp ? SocketType.Stream : SocketType.Dgram;
                IP = match.Groups["ip"].Value;
                Port = int.Parse(match.Groups["port"].Value);

                Parity m_parity = Parity.None;
                switch (match.Groups["parity"].Value.ToUpper())
                {
                    case "N": m_parity = Parity.None; break;
                    case "E": m_parity = Parity.Even; break;
                    case "M": m_parity = Parity.Mark; break;
                    case "S": m_parity = Parity.Space; break;
                    case "O": m_parity = Parity.Odd; break;
                }
                Parity = m_parity;
            }
        }

        public void Open()
        {
            ThreadPool.QueueUserWorkItem(state => createServer());
        }

        public void Close()
        {

        }


        void createServer()
        {
            try
            {
                //定义一个套接字用于监听客户端发来的消息，包含三个参数（IP4寻址协议，流式连接，Tcp协议）  
                ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType, ProtocolType);
                //服务端发送信息需要一个IP地址和端口号  
                IPAddress address = IPAddress.Parse(IP);
                //将IP地址和端口号绑定到网络节点point上  
                IPEndPoint point = new IPEndPoint(address, Port);
                //此端口专门用来监听的  

                //监听绑定的网络节点  
                ServerSocket.Bind(point);

                //将套接字的监听队列长度限制为20  
                ServerSocket.Listen(20);

                //负责监听客户端的线程:创建一个监听线程  
                Thread threadwatch = new Thread(watchconnecting);

                //将窗体线程设置为与后台同步，随着主线程结束而结束  
                threadwatch.IsBackground = true;

                //启动线程     
                threadwatch.Start();

                //Console.WriteLine("开启监听。。。");
                //Console.WriteLine("点击输入任意数据回车退出程序。。。");
                //Console.ReadKey();
                //Console.WriteLine("退出监听，并关闭程序。");
                connectSerialPort();

                LogHelper.Info("服务启动成功! [{0}]", Config);
            }
            catch (Exception ex)
            {
                LogHelper.Info($"服务启动失败! [{Config}]", ex);
            }
        }

        //监听客户端发来的请求  
        void watchconnecting()
        {
            Socket connection = null;

            //持续不断监听客户端发来的请求     
            while (true)
            {
                try
                {
                    connection = ServerSocket.Accept();
                }
                catch (Exception ex)
                {
                    //提示套接字监听异常     
                    LogHelper.Error(ex);
                    break;
                }

                //获取客户端的IP和端口号  
                IPAddress clientIP = (connection.RemoteEndPoint as IPEndPoint).Address;
                int clientPort = (connection.RemoteEndPoint as IPEndPoint).Port;

                //让客户显示"连接成功的"的信息  
                //string sendmsg = "连接服务端成功！\r\n" + "本地IP:" + clientIP + "，本地端口" + clientPort.ToString();
                //byte[] arrSendMsg = Encoding.GetEncoding("gb2312").GetBytes(sendmsg);
                //connection.Send(arrSendMsg);

                //客户端网络结点号  
                string remoteEndPoint = connection.RemoteEndPoint.ToString();
                //显示与客户端连接情况
                LogHelper.Debug($"客户端[{remoteEndPoint}]连接成功!");
                //添加客户端信息  
                clientConnectionItems.Add(remoteEndPoint, connection);

                //IPEndPoint netpoint = new IPEndPoint(clientIP,clientPort); 
                IPEndPoint netpoint = connection.RemoteEndPoint as IPEndPoint;

                //创建一个通信线程      
                ParameterizedThreadStart pts = new ParameterizedThreadStart(recv);
                Thread thread = new Thread(pts);
                //设置为后台线程，随着主线程退出而退出 
                thread.IsBackground = true;
                //启动线程     
                thread.Start(connection);
            }
        }

        /// <summary>
        /// 接收客户端发来的信息，客户端套接字对象
        /// </summary>
        /// <param name="socketclientpara"></param>    
        void recv(object socketclientpara)
        {
            Socket socketServer = socketclientpara as Socket;

            while (true)
            {
                //创建一个内存缓冲区，其大小为1024*1024字节  即1M     
                byte[] arrServerRecMsg = new byte[1024 * 1024];
                //将接收到的信息存入到内存缓冲区，并返回其字节数组的长度    
                try
                {
                    int count = socketServer.Available;
                    if (count > 0)
                    {
                        byte[] buffer = new byte[count];
                        var len = socketServer.Receive(buffer, count, SocketFlags.None);
                        //接收数据
                        string data = Encoding.GetEncoding("gb2312").GetString(buffer, 0, count);
                        LogHelper.Debug("从[{0}]接收数据: {1} ({2})",
                            socketServer.RemoteEndPoint.ToString(), ConvertHepler.ByteToHex(buffer), data);
                        //向串口发送数据
                        connectSerialPort();
                        SerialPort.Write(buffer, 0, count);
                    }
                }
                catch (Exception ex)
                {
                    LogHelper.Error(ex);

                    clientConnectionItems.Remove(socketServer.RemoteEndPoint.ToString());
                    Console.WriteLine("Client Count:" + clientConnectionItems.Count);
                    //提示套接字监听异常  
                    Console.WriteLine("客户端" + socketServer.RemoteEndPoint + "已经中断连接" + "\r\n" + ex.Message + "\r\n" + ex.StackTrace + "\r\n");
                    //关闭之前accept出来的和客户端进行通信的套接字 
                    socketServer.Close();
                    break;
                }
            }
        }

        void connectSerialPort()
        {
            if (SerialPort == null)
            {
                SerialPort = new SerialPort(PortName, BaudRate, Parity, DataBits, StopBits);
                SerialPort.DataReceived += SerialPort_DataReceived;
            }
            if (!SerialPort.IsOpen)
            {
                try
                {
                    SerialPort.Open();
                    LogHelper.Info($"串口[{PortName}]打开成功!");
                }
                catch (Exception ex)
                {
                    LogHelper.Error($"串口[{PortName}]打开失败!", ex);
                }
            }
        }

        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            var sp = sender as SerialPort;
            int count = sp.BytesToRead;
            byte[] buffer = new byte[count];
            sp.Read(buffer, 0, count);
            string data = Encoding.GetEncoding("gb2312").GetString(buffer, 0, count);
            List<string> deleteClients = new List<string>();
            foreach (var item in clientConnectionItems)
            {
                LogHelper.Debug("向[{0}]发送数据: {1} ({2})",
                    item.Key, ConvertHepler.ByteToHex(buffer), data);
                try
                {
                    item.Value.Send(buffer);
                }
                catch (SocketException ex)
                {
                    var errorcode = ex.SocketErrorCode;
                    if (errorcode == SocketError.ConnectionRefused
                        || errorcode == SocketError.ConnectionReset
                        || errorcode == SocketError.ConnectionAborted
                        || errorcode == SocketError.HostNotFound
                        || errorcode == SocketError.NoData
                        || errorcode == SocketError.NotSocket)
                    {
                        deleteClients.Add(item.Key);
                    }
                }
            }
            foreach (var item in deleteClients)
            {
                LogHelper.Error($"客户端[{item}]断开连接!");
                clientConnectionItems.Remove(item);
            }
        }
    }
}
